package com.github.wj.orm.security.repository.impl;

import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import static com.google.common.collect.Iterables.toArray;


/**
 * 自定义一个分页方法
 * Created by 王建 on 2017/6/9.
 */
public class CustomSpecs {

    public static <T>Specification<T> byAuto(final EntityManager entityManager, final T example){
        /**获得当前实体类的对象类型**/
        if(null==example){
            return new Specification<T>() {
                @Override
                public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                    return criteriaBuilder.conjunction();
                }
            };
        }else {
            final Class<T> type=(Class<T>) example.getClass();
            return new Specification<T>() {
                @Override
                public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                    List<Predicate> predicates=new ArrayList<>();
                    EntityType<T> entityType=entityManager.getMetamodel().entity(type);
                    for (Attribute<T,?> attr:entityType.getDeclaredAttributes()){
                        String name=attr.getName();
                        Object attrValue=getValue(example,name);
                        if(null!=attrValue){
                            if(attr.getJavaType()==String.class){
                                if(!StringUtils.isEmpty(attrValue)){
                                    predicates.add(criteriaBuilder.like(root.get(attribute(entityType,attr.getName(),String.class)),pattern((String)attrValue)));
                                }
                            }else{
                                predicates.add(criteriaBuilder.equal(root.get(attribute(entityType,attr.getName(),attrValue.getClass())),attrValue));
                            }
                        }
                    }
                    return predicates.isEmpty()?criteriaBuilder.conjunction():criteriaBuilder.and(toArray(predicates,Predicate.class));
                }
                /**
                 * 获取实体类的当前属性SingularAttribute,
                 * SingularAttribute中包含的是实体类中某个单独属性
                 * @param entityType
                 * @param fieldName
                 * @param fieldClass
                 * @param <T>
                 * @param <E>
                 * @return
                 */
                private <T,E>SingularAttribute<T,E> attribute(EntityType<T> entityType,String fieldName,Class<E> fieldClass){
                    return entityType.getDeclaredSingularAttribute(fieldName,fieldClass);
                }
                /**
                 * 获取实体对象属性值
                 * @param example
                 * @param name
                 * @return
                 */
                private Object getValue(T example,String name){
                    Object attrValue=null;
                    if(!StringUtils.isEmpty(name)){
                        name = name.substring(0, 1).toUpperCase() + name.substring(1);
                        try {
                            Method method=type.getMethod("get"+name);
                            attrValue=method.invoke(example);
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                    return attrValue;
                }
            };
        }
    }
    /**
     * 字符添加模糊查询通配符
     * @param str
     * @return
     */
    static private String pattern(String str){
        return "%"+str+"%";
    }
}
